```tex
\newcommand{\commonfolder}{../../common-files}

\input{\commonfolder/header}
\input{\commonfolder/copyright}


\newcommand{\dnsFigs}{./Figs}
\lhead{\bfseries SEED Labs -- DNSSEC Lab}


\def \code#1 {\fbox{\scriptsize{\texttt{#1}}}}

\newcommand{\bankcom}{\url{bank32.com}\xspace}
\newcommand{\wwwbank}{\url{www.bank32.com}\xspace}
\newcommand{\examplenet}{\url{example.net}\xspace}
\newcommand{\wwwexample}{\url{www.example.net}\xspace}

\begin{document}

\begin{center}
{\LARGE DNS 安全扩展（DNSSEC）实验}
\end{center}

\seedlabcopyright{2022}


% *******************************************
% SECTION
% ******************************************* 
\section{实验概述}

为了保护域名系统 (DNS)，开发了 DNS 安全扩展 (DNSSEC)。DNSSEC 是对 DNS 的一组扩展，旨在提供 DNS 数据的认证和完整性检查。
在 DNSSEC 中，所有来自受保护区域的回答都会进行数字签名。通过检查数字签名，DNS 解析器可以验证信息是否真实有效。有了这样的机制，
DNS 缓存投毒攻击将被击败，因为任何伪造的数据（无论是从欺骗响应包还是权威名称服务器获取的）都将因无法通过签名验证而被检测出来。

本实验旨在帮助学生理解 DNSSEC 的工作原理。给定一个简化的 DNS 基础设施后，他们的任务是配置每个名称服务器，使其支持 DNSSEC。
该实验涵盖了以下主题：

\begin{itemize}[noitemsep]
    \item DNS 及其工作方式
    \item DNSSEC 及其实现方式
    \item 密钥管理、公钥、数字签名
\end{itemize}

\paragraph{参考文献和视频。}
有关 DNS 协议及攻击的详细覆盖信息可以在以下资源中找到：

\begin{itemize}
    \item 《SEED Book》第11章，\seedisbook
    \item 《SEED Lecture》第七节，\seedisvideo
\end{itemize}

\paragraph{实验环境。} 
\seedenvironmentB
\nodependency

% *******************************************
% SECTION
% ******************************************* 
\section{实验环境设置}

在实验环境中，我们提供了一个简化的 DNS 基础设施，包括根服务器、顶级域 \texttt{edu} 的域名服务器、教育网 \texttt{example.edu} 的域名服务器和本地 DNS 服务器。
这些名称服务器中的每一个都托管在一个容器中。为了简化起见，在同一局域网（见图~\ref{dnssec:fig:labsetup-simple}) 中，我们将所有容器放置在同一局域网上。
这是一种简化的设置，因为在真实世界中，这些名称服务器并不在同一个局域网上。然而，无论它们是在同一个局域网上还是分散在网络中，这些名称服务器的配置是相似的。

如果学生在此之前已经完成了 DNS 基础设施实验，则可以选择使用该实验中的设置而不是本实验中的简化设置，因为那个设置以互联网仿真器为基础，更为现实。

\begin{figure}[htb]
    \centering
    \includegraphics[width=0.90\textwidth]{\dnsFigs/labsetup.pdf}
    \caption{实验环境设置}
    \label{dnssec:fig:labsetup-simple}
\end{figure}

\paragraph{软件安装。} 我们将使用一些尚未在 SEED 虚拟机中安装的工具，请使用以下命令进行安装：

\begin{lstlisting}
$ sudo apt-get install bind9utils
\end{lstlisting}

本实验所需的 DNSSEC 命令包含在上面安装的软件包中。我们仅在主机虚拟机上安装这些命令，而不在容器内部。但是，需要将命令的结果复制到相应的容器内。
我们可以使用 \texttt{"docker cp"} 命令在主机与容器之间复制文件：

\begin{lstlisting}
// 将 xyz 文件复制到容器的 /tmp 目录
$ docker cp xyz <docker id>:/tmp

// 从容器中复制文件 /tmp/abc
$ docker cp <docker id>:/tmp/abc . 
\end{lstlisting}

% *******************************************
% SECTION
% ******************************************* 
\section{任务1：设置 \texttt{example.edu} 域}

在本任务中，我们将进入托管 \texttt{example.edu} 名称服务器的容器文件夹。该容器托管了 \texttt{example.edu} 域。
我们需要修改此文件夹中的文件，使名称服务器能够支持 DNSSEC 查询。

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务1.a：生成 \texttt{example.edu} 服务器的密钥对}

首先，我们需要生成一对密钥，一个称为区域签名密钥（ZSK），另一个称为键签名密钥 (KSK)。每个密钥都包含一个公钥和一个私钥。
ZSK 用于签署区域记录，而 KSK 用于签署 ZSK。

这种双重密钥方案在密钥管理中非常常见。实际对记录进行签署的密钥（即 ZSK）可以频繁更换以实现更好的安全性。如果只使用一个密钥，
当此密钥更改时，需要更新密钥管理系统中的数据。在 DNSSEC 中，父区域存储关于该密钥的信息，因此需要更新父区域文件。

为了避免这种开销，引入了第二个密钥，称为键签名密钥（KSK），它仅用于签署区域文件中的 ZSK 记录，而其他记录仍然由 ZSK 签署。
KSK 的信息提供给父区域，但 KSK 并不会频繁更改。当 ZSK 更改时，我们只需使用 KSK 生成新签名即可。

ZSK 和 KSK 都是公钥对，但由于它们的角色和生命周期不同，通常 KSK 密钥的强度大于 ZSK 密钥，即密钥长度更长。
在下面的命令中，第一个命令使用 \texttt{RSASHA256} 算法并设置密钥大小为 1024 来生成一个 ZSK。第二个命令也使用相同算法，并将密钥大小加倍来生成 KSK。
如果您的操作系统找不到该命令，则忘记安装所需的软件（参见实验环境设置部分）。

\begin{lstlisting}
dnssec-keygen -a RSASHA256 -b 1024 example.edu
dnssec-keygen -a RSASHA256 -b 2048 -f KSK example.edu
\end{lstlisting}

第二个命令生成了一个 KSK。 \texttt{-f} 选项将 \texttt{257} 写入密钥文件，表示这是 KSK。
如果没有此选项，则默认值 \texttt{256} 将写入密钥文件，表示这是 ZSK。

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务1.b：签署 \texttt{example.edu} 域的区域文件}

我们将使用以下 \texttt{dnssec-signzone} 命令对区域文件进行签名：

\begin{lstlisting}
dnssec-signzone -e 20501231000000 -S -o example.edu example.edu.db
\end{lstlisting}

带有 \texttt{-S} 选项，该命令将在指定的文件夹中查找区域签名密钥和键签名密钥。由于未使用 \texttt{-K} 选项指明密钥文件夹，
默认情况下会在此文件夹下查找这些密钥。
如果您在指定文件夹中有多个密钥对，则此命令将为每个密钥生成一个签名，因此您会为每个记录获得多个签名。

\texttt{-e} 选项用于设置签名的过期时间。默认情况下，过期时间为一个月。因此，
如果不使用 \texttt{-e} 选项，在一个月后检查您的配置时将不再有效。对学生而言，一个月应该足够了，但对于将来可能重用答案的教师，
设置较长的过期时间可以避免不必要的麻烦。
过期时间格式为 \texttt{YYYYMMDDHHMMSS}。在示例中，我们将过期时间设为 2050 年。

一旦区域文件被签署，一个新的以 \texttt{.signed} 结尾的区域文件将被生成。请查看该文件，并描述原始区域文件中新添加的内容及其目的。

此新区域文件将由我们的名称服务器使用，因此我们需要修改 \texttt{named.conf.seedlabs} 文件来告诉名称服务器使用这个文件作为其区域文件。
以下是一个例子：

\begin{lstlisting}
zone "example.edu" {
        type master;
        file "/etc/bind/example.edu.db.signed";
};
\end{lstlisting}

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务1.c：测试}

使用 \texttt{docker-compose} 命令，我们可以构建并启动所有容器。然后我们使用以下 \texttt{dig} 命令进行测试。
该命令允许直接询问服务器（\texttt{@server}）来获取特定类型的 DNS 记录（\texttt{name type}），用于特定的域名或主机名 (\texttt{name})。
通过使用 \texttt{+dnssec} 标志，我们在查询附加部分的 OPT 记录中设置 DNSSEC 检查标志 (DO)，请求服务器发送相关的 DNSSEC 记录，如签名。

\begin{lstlisting}
通用格式：
$ dig @server name type +dnssec

示例：
$ dig @10.9.0.65 example.edu DNSKEY +dnssec
\end{lstlisting}

运行以下命令从 \texttt{example.edu} 名称服务器获取不同类型的数据记录，并为响应中的每个记录提供解释。
在我们的设置中，\texttt{10.9.0.65} 是分配给 \texttt{example.edu} 名称服务器的 IP 地址。

\begin{lstlisting}
$ dig @10.9.0.65 example.edu DNSKEY +dnssec
$ dig @10.9.0.65 example.edu NS +dnssec
$ dig @10.9.0.65 www.example.edu A +dnssec
\end{lstlisting}

% *******************************************
% SECTION
% ******************************************* 
\section{任务2：设置 \texttt{edu} 服务器}

在本任务中，我们将配置 \texttt{edu} 服务器。指令与任务1类似。

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务2.a：查找并添加 DS 记录}

在对 \texttt{example.edu} 的区域文件进行签名后会生成一个用于键签名密钥 (KSK) 的 DS 记录。默认情况下，它被放置在一个名为 \texttt{dsset-<xyz>} 的文件中，
其中 \texttt{<xyz>} 是区域名称。在我们的情况下，\texttt{dsset-example.edu} 是文件名。

DS（Delegation Signer）记录包含一个被委托区的名称，并引用子区中的 DNSKEY 记录。
DS 记录应与委派 NS 记录一起放在父区域中。
以下是一个 DS 记录的例子：

\begin{lstlisting}
example.edu.   IN DS 10246   8  2   (*@\textbf{563D...(omitted)...1D59D1}@*)
                       (*@\ding{192}@*)              (*@\ding{193}@*)
\end{lstlisting}

DS 记录包含一个键标签（标记为 \ding{192}），这是一个标识引用密钥的短数字值。这个密钥是该域的键签名密钥 (KSK)。
DS 记录中最重要的是摘要（用 \ding{193} 标记），它是 KSK 的单向哈希值。通过将此摘要值放入父区域（即 \texttt{edu} 区域），
可以验证子区的键签名密钥的完整性。这就是 DS 记录的主要目的。

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务2.b：配置 \texttt{edu} 服务器}

按照任务1中的指令生成此域名的 ZSK 和 KSK 密钥，然后使用这些密钥签署区域文件。
在签署区域文件之前，我们需要向其中添加一条记录。
该记录是子委托区域的 DS 记录。由于 \texttt{example.edu} 区域被委托给了另一个名称服务器，
因此其键签名密钥的摘要必须包含在 \texttt{edu} 区域中；这样可以验证密钥的完整性。

我们可以通过复制并粘贴 DS 记录的内容到 \texttt{edu} 的区域文件，或使用以下 \texttt{INCLUDE} 条目来包含该文件。后者
方法更适合实验，因为即使我们更改了某个区域的键签名密钥，DS 记录的文件名保持不变，因此不需要修改父区域文件。

\begin{lstlisting}
$INCLUDE ../edu.example/dsset-example.edu.
\end{lstlisting}

% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{任务2.c：测试} 

构建并运行所有容器。然后运行以下命令从 \texttt{edu} 名称服务器获取不同类型的数据记录，并为响应中的每个记录提供解释。
在我们的设置中，\texttt{10.9.0.60} 是分配给 \texttt{edu} 名称服务器的 IP 地址。

\begin{lstlisting}
$ dig @10.9.0.60 edu DNSKEY +dnssec
$ dig @10.9.0.60 edu NS +dnssec
$ dig @10.9.0.60 example.edu +dnssec
\end{lstlisting}

% *******************************************
% SECTION
% *******************************************
\section{任务3：配置根服务器} 

设置根服务器的过程与前一个任务相同。在签署区域文件之前，请务必添加 \texttt{edu} 区域的 DS 记录。

\paragraph{测试。}
运行以下命令从根名称服务器获取不同类型的数据记录，并为响应中的每个记录提供解释。
在我们的设置中，\texttt{10.9.0.30} 是 
分配给根名称服务器的
IP 地址。

\begin{lstlisting}
$ dig @10.9.0.30 . DNSKEY +dnssec
$ dig @10.9.0.30 . NS +dnssec
$ dig @10.9.0.30 edu +dnssec
$ dig @10.9.0.30 example.edu +dnssec
\end{lstlisting}

% *******************************************
% SECTION
% *******************************************
\section{任务4：配置本地 DNS 服务器} 

当计算机需要从主机名解析 IP 地址（或反之亦然）时，它会向其辅助名称服务器发送请求，称为本地 DNS 服务器（不需要是本地的）。这个本地 DNS 服务器将完成整个 DNS 解析过程，
然后将结果返回给计算机。

在 DNSSEC 中，每个名称服务器都会向客户端提供自己的公钥（ZSK 和 KSK），因此客户端可以使用 KSK 验证 ZSK 的签名，并使用 ZSK 验证每条记录的签名。这留下了一个不确定的问题：如何验证 KSK 的真实性。
这就是父区域中的 DS 记录的目的。父区域将为其子区域提供 DS 记录，用于验证子域的密钥。

由于根服务器没有父区，我们如何验证根服务器的真实性？ 根服务器的公钥是信任的基础，因此它们在 DNSSEC 基础设施中是最关键的密钥，并被称为 \textit{信任锚}。必须以安全的方式获取这些公钥。

BIND 9 包含内置的 DNSSEC 信任锚，但可以通过 \path{/etc/bind/bind.keys} 内容覆盖。
在本任务中，我们将把根服务器的公钥放入此文件中。请务必在此处使用 KSK 而不是 ZSK。

\begin{lstlisting}
trust-anchors {
    . initial-key 257 3 8 " <Root's Key Signing Key> ";
};
\end{lstlisting}

我们还需要在本地 DNS 服务器上启用 DNSSEC，以便它执行 DNSSEC 验证。只需修改 \texttt{named.conf.options} 文件，将 DNSSEC 条目更改为以下内容：

\begin{lstlisting}
dnssec-validation auto;
dnssec-enable yes;
\end{lstlisting}

\paragraph{测试。} 在启动本地服务器容器后，运行以下命令（在我们的设置中，\texttt{10.9.0.53} 是分配给本地 DNS 服务器的 IP 地址）。

\begin{lstlisting}
$ dig @10.9.0.53 www.example.edu +dnssec
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: (*@\textbf{NOERROR}@*), id: 37130
;; flags: qr rd ra (*@\textbf{ad}@*); QUERY: 1, (*@\textbf{ANSWER: 2}@*), AUTHORITY: 0, ...

;; ANSWER SECTION:
www.example.edu.        259200  IN      A       1.2.3.5
www.example.edu.        259200  IN      RRSIG   ... <signature>
\end{lstlisting}

如果一切设置正确，您应该能够获取到 \url{www.example.edu} 的 IP 地址的答案（如果回复是真实有效的）。注意 \texttt{ad} 标志，表示“真实数据”。如果此标志被设置，则回复中的所有记录都是真实的，即 DNSSEC 验证成功。如果没有启用 DNSSEC，您仍然会收到答案，
但这个标志不会被设置。

如果回复不真实有效，您将收到错误消息。请设计一个实验来演示当回复为假时本地 DNS 服务器能够检测到并显示错误消息。在此实验中不需要进行真实的攻击。
您可以直接修改现有的某些记录而无需重新生成签名（也可以损坏一个签名）。

% *******************************************
% SECTION
% *******************************************
\section{任务5：添加另一个域名名称服务器} 

假设您拥有 \texttt{edu} 内的一个域名，并且希望为此域设置一个支持 DNSSEC 的名称服务器。我们已经为这个目的创建了一个备用容器（该容器上已安装了 BIND9 服务器）。
您的任务是配置名称服务器，使其支持 DNSSEC。
此域的名称应包含您姓氏和实验进行的年份。例如，如果您的姓氏为 Smith 而且这一年是2022年，则域名应为 \texttt{smith2022.edu}。
无需在现实世界中实际拥有这个域名。我们仅在一个实验室环境中的 DNS 基础设施内设置它。

% *******************************************
% SECTION
% ******************************************* 
\section{提交}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input{\commonfolder/submission}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


\end{document}
```